import { Ipc } from "@main/helper/ipcMain";
import { Ipc_ChannelEnum } from "@enum/channel.enum";
import { ElectronEventEnum } from "@enum/electron.enum";
import { baseBrowserWindowCfg, baseWebPreferences } from "@main/config";
import {
  BrowserWindow,
  type WebContents,
  app,
  type BrowserWindowConstructorOptions,
  dialog,
} from "electron";
import { getAllWindows, getWinById } from "@main/helper/handle";
import { useProcessException } from "@main/hooks/exception-hook";

import { isNullorUnDef } from "@utils/judge";
import { Win_Base_Func_Enum,Win_Base_Status_Enum,Win_Top_Types_Enum } from "@enum/app.enum";


const { childProcessGone } = useProcessException();


export interface WindowDefaultCfg {
  defaultUrl: string;
}

export class WindowService {
  //拦截渲染进程以winodw.open 或 target=_blank 创建窗口
  static renderOpenWinHandle(win: BrowserWindow): void {
    let fn = (webcontents: WebContents) => {
      webcontents?.setWindowOpenHandler(({ url }) => {
        const windowInstance = new Window();
        windowInstance.createWindow({
          url,
        });
        return { action: "deny" };
      });
    };

    if (win) {
      fn(win.webContents);

      //当渲染进程创建webview时 也监听webview内部直接创建窗口
      win.webContents.on(
        ElectronEventEnum.DID_ATTACH_WEBVIEW,
        (_, webContents) => fn(webContents)
      );
    }
  }

  //渲染进程dom挂载完成后发送数据
  static sendWindowLoad(win: BrowserWindow, params: Customize): void {
    win?.webContents.on(ElectronEventEnum.DID_FINISH_LOAD, () =>
      win.webContents.send(Ipc_ChannelEnum.WIN_LOAD, params)
    );
  }

  //监听close事件
  static listenWinCloseEvent(win: BrowserWindow):void{
    win.on('close',()=>{

      //给父窗口获焦
      const parentId = Window.getCustomizeById(win.id)?.parentId
      if(parentId)getWinById(parentId)?.focus()
      
      //从CustomizeMap中删除
      Window.clearCustomizeSetById(win.id)
    })
  }

  //监听假死
  static listenUnResponsive(win: BrowserWindow):void{
    win.on('unresponsive',()=>{
      dialog
      .showMessageBox(win, {
        type: "warning",
        title: "警告",
        buttons: ["重载", "退出"],
        message: "图形化进程失去响应，是否等待其恢复？",
        noLink: true,
      })
      .then((res) => {
        if (res.response === 0) win.reload();
        else win.close();
      });
    })
  }


  //显示窗口
  static showWindow(): void {
    Ipc.on<[number]>(Ipc_ChannelEnum.WIN_SHOW, (_, id) => {
       Window.func(Win_Base_Func_Enum.SHOW,id);
    });
  }

  //创建窗口
  static createWindow(): void {
    Ipc.handle<[BrowserWindowConstructorOptions?,Customize?]>(Ipc_ChannelEnum.WIN_CREATE, async ( _, bwCfg , customize ) => {
      const windowInstance = new Window();
      const win = await windowInstance.createWindow(
        customize,
        bwCfg
      );
      return {
        winId: win?.id,
        webContentId: win?.webContents?.id,
      };
    });
  }

  //窗口关闭、隐藏、显示，等常用方法
  static funcWindow(): void {
    Ipc.on<[Win_Base_Func_Enum,number?]>(
      Ipc_ChannelEnum.WIN_FUNC,
      async (_, ...args ) => {
        Window.func(...args)
      }
    );
  }

  //查看窗口状态常用方法
  static statusWindow(): void {
    Ipc.on<[Win_Base_Status_Enum,number]>(
      Ipc_ChannelEnum.WIN_FUNC,
      async (_, ...args ) => {
        Window.func(...args)
      }
    );
  }

  //设置窗口是否置顶
  static setAlwaysOnTop(): void {
    Ipc.on<[{
      id: number;
      is: boolean;
      type?: Win_Top_Types_Enum;
    }]>(
      Ipc_ChannelEnum.WIN_UP,
      async (_, ...args ) => {
        Window.setAlwaysOnTop(...args)
      }
    );
  }


  static on() {
    this.createWindow();
    this.showWindow();
    this.funcWindow();
    this.statusWindow();
    this.setAlwaysOnTop();
  }
}

export class Window {
  private static _instance: Window;

  //定制参数set合集
  private static _customizeMap: Map<number, Customize> = new Map();

  //默认配置
  private static _defaultCfg: Nullable<WindowDefaultCfg> = null;

  static get defaultCfg(): Nullable<WindowDefaultCfg> {
    return this._defaultCfg;
  }

  static set defaultCfg(val: Nullable<WindowDefaultCfg>) {
    this._defaultCfg = val;
  }

  //单例
  static factory(): Window {
    if (!this._instance) this._instance = new this();
    return this._instance;
  }

  //根据id清空customizeSet
  static clearCustomizeSetById(id: number): void {
    Window._customizeMap.delete(id);
  }

  //获取customize
  static getCustomizeById(id: number): Customize | undefined {
    return Window._customizeMap.get(id);
  }

  //合并传入窗口参数
  static useBwCfgHanlde(
    bwCfg: BrowserWindowConstructorOptions = {},
    customize?:Customize
  ): BrowserWindowConstructorOptions {
    bwCfg = Object.assign(baseBrowserWindowCfg, bwCfg);
    bwCfg.webPreferences = Object.assign(
      baseWebPreferences,
      bwCfg.webPreferences
    );
    !app.isPackaged && (bwCfg.webPreferences.devTools = true);

    if(customize?.parentId) bwCfg.parent = getWinById(customize?.parentId)!

    return bwCfg;
  }
  

  //合并传入窗口定制
  static useCustomizeHandle(win: Window, original: Customize = {}): Customize {
    const customize = Object.assign(
      {
        id: win.browser?.id,
        url: Window.defaultCfg?.defaultUrl,
        route: "/home",
        title: "simple-electron",
        isPackaged: app.isPackaged,
        isMain: false,
        show: false,
        silenceFunc: true,
      },
      original
    );

    //设置窗口标题
    win.browser?.setTitle(customize.title);

    return customize;
  }

  //获取主窗口
  static getMainWindow(): BrowserWindow {
    let win: Nullable<BrowserWindow> = null;

    //item是customiz index 是id
    this._customizeMap.forEach((item, index) => {
      if (item && (index === 1 || item.isMain)) win = getWinById(item.id!);
    });

    if (!win)
      throw new Error("未获取到主窗口，请查看初始窗口定制参数isMain是否为true");
    return win;
  }


  //窗口常用方法
  static func(type: Win_Base_Status_Enum, id: number): void;
  static func(type: Win_Base_Func_Enum, id?: number): void;
  static func(type: Win_Base_Status_Enum | Win_Base_Func_Enum, id?: number): void {
    const fn = (win: BrowserWindow) => {
      if (this._customizeMap.get(win.id)?.silenceFunc) {
        win[type]();
        if(this._customizeMap.get(win.id)![type]){
          this._customizeMap.get(win.id)![type] = true;
        }
      }
    };

    if (!isNullorUnDef(id)) {
      const win = getWinById(id)!;
      fn(win);
      return;
    }
    for (const i of getAllWindows()) {
      fn(i);
    }
  }

  //设置窗口是否置顶
  static setAlwaysOnTop(args: {
    id: number;
    is: boolean;
    type?: Win_Top_Types_Enum;
  }): void {
    const win = getWinById(args.id);
    if (win) win.setAlwaysOnTop(args.is, args.type || "normal");
  }

  //窗口定制
  private _customize?: Customize;

  //窗口
  public browser?: BrowserWindow;

  constructor() {}

  //创建窗口
  async createWindow(
    customize?: Customize,
    bwCfg?: BrowserWindowConstructorOptions
  ): Promise<BrowserWindow> {
    try {
      //如果是单窗口路由 且加载url没提供 则先去唤醒已打开的同路由窗口
      if (customize?.isOneWindow && !customize?.url) {
        Window._customizeMap.forEach((v) => {
          if (v?.route === customize?.route && v?.id) {
            let identical = getWinById(v.id);
            if (identical) {
              identical.focus();
              return identical;
            } else Window.clearCustomizeSetById(v.id);
          }
        });
      }
      if ((this.browser = new BrowserWindow(Window.useBwCfgHanlde(bwCfg,customize)))) {
        return await this.load(customize!);
      } else throw new Error("渲染进程bwCfg无效！");
    } catch (err) {
      throw new Error("渲染进程创建失败：" + err);
    }
  }

  //加载渲染层
  load(customize: Customize): Promise<BrowserWindow> {
    return new Promise((res, rej) => {
      if ((this._customize = Window.useCustomizeHandle(this, customize))) {
        let url = customize?.url ?? Window.defaultCfg?.defaultUrl;
        if (!url) rej("未设置窗口Url");
        else {
          //等页面挂载完成发送窗口定制数据
          WindowService.sendWindowLoad(this.browser!, this._customize);
          //监听渲染进程直接创建窗口
          WindowService.renderOpenWinHandle(this.browser!);
          //监听窗口close事件
          WindowService.listenWinCloseEvent(this.browser!);
          //假死监听
          WindowService.listenUnResponsive(this.browser!);
          //其他进程崩溃监听
          childProcessGone(this.browser!);
          
          const fn = this._customize?.isPackaged ? "loadFile" : "loadURL";
          //加载url
          this.browser?.[fn](url)
            .then(() => {
              Window._customizeMap.set(this._customize?.id!, this._customize!);

              console.log("渲染进程创建成功！id：" + this._customize?.id);

              !this._customize?.isPackaged &&
                this.browser?.webContents.openDevTools({ mode: "detach" });
              res(this.browser!);
            })
            .catch((err) => rej(err));
        }
      } else rej("渲染进程customize无效！");
    });
  }
}

export const WindowInstance = Window.factory();
